using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Action.SPI;
//using T.FAS.Authority.MetaData.Dev.Plugin;
//using T.FAS.Authority.Metadata.Entity;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.Engine;
using T.FAS.Runtime.DataAccess.Service;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Expression.Entity;
using T.FAS.Runtime.Expression.Service;
using T.FAS.Runtime.Metadata.Client;

namespace T.FAS.Runtime.Action.Engine
{
    public class CDOBatchActionEngine : IActionExecuteProxy, IActionDataResultBuilder, IActionExecuteHandler
    {
        public TResult BuildActionDataResult(ActionExecContext context, object dataResult)
        {
            return null;
        }

        public object Execute(ActionExecContext context)
        {
            return null;
        }

        public object ExecuteWithSubEngine(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            if (context.ActionType == SPI.ActionType.CDO_BatchDelete)
            {
                return CDOBatchDeleteExecute(subEngine, context);
            }
            else if (context.ActionType == SPI.ActionType.CDO_BatchEditUpdate)
            {
                return CDOBatchEditUpdateExecute(subEngine, context);
            }
            else if (context.ActionType == SPI.ActionType.CDO_BatchCreate)
            {
                return CDOBatchCreateExecute(subEngine,context);
            }
            else if (context.ActionType == SPI.ActionType.CDO_BatchUpdate)
            {
                return CDOBatchUpdateExecute(subEngine,context);
            }
            return null;
        }

        void IActionExecuteHandler.AfterExecute(ActionExecContext execContext)
        {
            if (execContext != null && execContext.MetaData != null)
            {
                var action = execContext.MetaData as ActionMetaData;
                if (action != null && action.ActionType == Metadata.Entity.ActionType.CDO &&
                    !string.IsNullOrEmpty(action.Method.AfterMethodContent))
                {
                    var parameters = execContext.DataContent;

                    var result = ExprService.GetInstance.Execute(action.Method.AfterMethodContent, execContext.FASContext, parameters);
                    if (result.State == ResultState.Fail)
                    {
                        throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ExpressionScriptExecFail, result.RowNum,result.ErrMsg));
                    }
                }
            }
        }

        void IActionExecuteHandler.BeforeExecute(ActionExecContext execContext)
        {
            if (execContext != null && execContext.MetaData != null)
            {
                var action = execContext.MetaData as ActionMetaData;
                if (action != null && action.ActionType == Metadata.Entity.ActionType.CDO &&
                    !string.IsNullOrEmpty(action.Method.BeforeMethodContent))
                {
                    var parameters = execContext.DataContent;

                    _ = action.RelatedMetaDataID;
                    ConstructChangeSet(execContext);

                    var result = ExprService.GetInstance.Execute(action.Method.BeforeMethodContent, execContext.FASContext, parameters);
                    if (result.State == ResultState.Fail)
                    {
                        throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ExpressionScriptExecFail, result.RowNum,result.ErrMsg));
                    }
                }
            }
        }

        void IActionExecuteHandler.ExecuteError(ActionExecContext execContext, Exception exception)
        {
            throw new NotImplementedException();
        }

        private static void ConstructChangeSet(ActionExecContext execContext)
        {
            var action = execContext.MetaData as ActionMetaData;
            if (action == null)
            {
                return;
            }
            var parameters = execContext.DataContent;
            execContext.FASContext = new FASContext();
            if (execContext.ActionType == SPI.ActionType.CDO_BatchUpdate)
                execContext.FASContext.CurrentRecordList = GetRecordListFromParams(parameters);
        }
        private static List<TRecord> GetRecordListFromParams(Dictionary<string, object> paramList)
        {
            var recordList = paramList["ListTRecord"];
            if (recordList == null)
                return new List<TRecord>();
            if (recordList is List<TRecord>)
                return recordList as List<TRecord>;
            else if (recordList is List<ExpandoObject>)
            {
                var dataList = new List<TRecord>();
                var tmpList = recordList as List<ExpandoObject>;
                tmpList.ForEach(t =>
                {
                    var tmp = JObject.Parse(JsonConvert.SerializeObject(t));
                    var data = TRecord.BuildRecord(tmp);
                    dataList.Add(data);
                });
                return dataList;
            }
            else if (recordList is List<TRecord>)
            {
                var dataList = new List<TRecord>();
                var tmpList = recordList as List<ExpandoObject>;
                tmpList.ForEach(t =>
                {
                    var tmp = JObject.Parse(Convert.ToString(t));
                    var data = TRecord.BuildRecord(tmp);
                    dataList.Add(data);
                });
                return dataList;
            }
            else
            {
                var dataList = new List<TRecord>();
                var tmpList = recordList as List<JObject>;
                if (tmpList == null)
                {
                    var recordListString = Convert.ToString(recordList);
                    tmpList = JsonConvert.DeserializeObject<List<JObject>>(recordListString.TrimStart('{').TrimEnd('}'));
                }
                tmpList.ForEach(t=>
                {
                    var data = TRecord.BuildRecord(t);
                    dataList.Add(data);
                });
                return dataList;
            }
        }


        private object CDOBatchEditUpdateExecute(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            if (context == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError,string.Format(CommonStringClassEngine.CDOBatchActionEngine_CDOBatchEditUpdateExecute));
            if (context.DependencyMetaDatas == null || context.DependencyMetaDatas.Count <= 0)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextDepenMetaCannotBeNull, "CDOBatchEditUpdateExecute"));
            var parameters = context.DataContent;
            var recordList = GetRecordListFromParams(parameters);
            var resultList = new List<TResult>();
            if (recordList != null && recordList.Count > 0)
            {
                foreach (var record in recordList)
                {
                    if (record.ChangeSet != null && record.ChangeSet.ChangeType == TRecordChangeType.UnChanged)
                        continue;
                    var subContext = context.Copy();
                    subContext.ActionType = SPI.ActionType.Action; 
                    var dependencyMetaData = GetDependencyMetaData(context.DependencyMetaDatas,record);
                    subContext.MetadataID = dependencyMetaData.MetaDataID;
                    subContext.MetadataCode = dependencyMetaData.MetaDataCode;
                    subContext.MetaData = dependencyMetaData.MetaDataContent;
                    subContext.DependencyMetaDatas = null;
                    subContext.IsBatchUpdateAction = true;
                    if (subContext.FASContext == null)
                        subContext.FASContext = new FASContext();
                    subContext.FASContext.CurrentRecord = record;
                    var cdo = subContext.OriginalActionMetaData as CompositeDataObject;
                    var pkID = "";
                    if (cdo!=null)
                        pkID = TRecordCommonExtension.GetPKValue(record,cdo.RootCompositeNodeObject);
                    if (!string.IsNullOrEmpty(pkID))
                    {
                        subContext.AddParamOverride("ID",pkID);
                    }
                    subContext.AddParamOverride("TRecord",record);
                    var result = subEngine.Execute(subContext);
                    resultList.Add(result);
                }
            }
            return resultList;
        }
        private object CDOBatchDeleteExecute(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            _ = context.DataContent;

            var deleteIds =JsonConvert.DeserializeObject<List<string>>(JsonConvert.SerializeObject(context.DataContent["IDS"]));

            var resultList = new List<TResult>();
            if (deleteIds != null && deleteIds.Count > 0)
            {
                var dependencyMetaData = context.DependencyMetaDatas[0];
                if(dependencyMetaData==null)
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextDepenMetaCannotBeNull, "CDOBatchDeleteExecute"));
                foreach (var id in deleteIds)
                {
                    var subContext = context.Copy();
                    subContext.ActionType = SPI.ActionType.Action;
                    subContext.MetadataID = dependencyMetaData.MetaDataID;
                    subContext.MetadataCode = dependencyMetaData.MetaDataCode;
                    subContext.MetaData = dependencyMetaData.MetaDataContent;
                    subContext.DependencyMetaDatas = null;
                    subContext.AddParamOverride("ID",id);
                    var result = subEngine.Execute(subContext);
                    resultList.Add(result);
                }
            }
            return resultList;
        }

        private object CDOBatchUpdateExecute(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            if (context == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextCannotBeNull));
            if (context.DependencyMetaDatas == null || context.DependencyMetaDatas.Count <= 0)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextDepenMetaCannotBeNull, "CDOBatchUpdateExecute"));
            var parameters = context.DataContent;
            var recordList = GetRecordListFromParams(parameters);
            var dependencyMetaData = context.DependencyMetaDatas[0];
            if (dependencyMetaData == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextDepenMetaCannotBeNull, "CDOBatchUpdateExecute"));
            var resultList = new List<TResult>();
            if (recordList != null && recordList.Count > 0)
            {
                foreach (var record in recordList)
                {
                    var subContext = context.Copy();
                    subContext.ActionType = SPI.ActionType.Action;
                    subContext.MetadataID = dependencyMetaData.MetaDataID;
                    subContext.MetadataCode = dependencyMetaData.MetaDataCode;
                    subContext.MetaData = dependencyMetaData.MetaDataContent;
                    subContext.DependencyMetaDatas = null;
                    subContext.IsBatchUpdateAction = true;
                    if (subContext.FASContext == null)
                        subContext.FASContext = new FASContext();
                    if (record.ChangeType == TRecordChangeType.UnChanged)
                    {
                        subContext.OriginalActionMetaData = context.OriginalActionMetaData;
                        subContext.MetaData = context.MetaData;
                        subContext.AddParamOverride("TRecord", record);
                        CDOActionEngine.ConstructChangeSet(subContext);
                    }
                    else
                    {
                        subContext.FASContext.CurrentRecord = record;
                        subContext.AddParamOverride("TRecord", record);
                    }
                    var result = subEngine.Execute(subContext);
                    resultList.Add(result);
                }
            }
            return resultList;

        }
        private object CDOBatchCreateExecute(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            if (context == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextCannotBeNull, "CDOBatchUpdateExecute"));
            if (context.DependencyMetaDatas == null || context.DependencyMetaDatas.Count <= 0)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextDepenMetaCannotBeNull, "CDOBatchUpdateExecute"));
            var parameters = context.DataContent;
            var recordList = GetRecordListFromParams(parameters);
            var dependencyMetaData = context.DependencyMetaDatas[0];
            if (dependencyMetaData == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExecFail_ContextDepenMetaCannotBeNull, "CDOBatchUpdateExecute"));
            var resultList = new List<TResult>();
            if (recordList != null && recordList.Count > 0)
            {
                foreach (var record in recordList)
                {
                    var subContext = context.Copy();
                    subContext.ActionType = SPI.ActionType.Action;
                    subContext.MetadataID = dependencyMetaData.MetaDataID;
                    subContext.MetadataCode = dependencyMetaData.MetaDataCode;
                    subContext.MetaData = dependencyMetaData.MetaDataContent;
                    subContext.DependencyMetaDatas = null;
                    subContext.IsBatchUpdateAction = true;
                    if (subContext.FASContext == null)
                        subContext.FASContext = new FASContext();
                    subContext.FASContext.CurrentRecord = record;
                    subContext.AddParamOverride("TRecord", record);
                    var result = subEngine.Execute(subContext);
                    resultList.Add(result);
                }
            }
            return resultList;
        }
        private FASMetaData GetDependencyMetaData(List<FASMetaData> metadatas, TRecord record)
        {
            if (record != null)
            {
                if (record.ChangeSet == null)
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.CDOBatchActionEngine_GetDependencyMetaData));

                var metadata = metadatas.Find(m => m.FASMetaDataTag.Equals(record.ChangeSet.ChangeType.ToString(), StringComparison.OrdinalIgnoreCase));
                return metadata;
            }
            return null;
        }
    }
}
